package de.bazinga.rotw.vehicle;

import com.jme.bounding.BoundingBox;
import com.jme.input.KeyBindingManager;
import com.jme.input.action.InputActionEvent;
import com.jme.math.Matrix3f;
import com.jme.math.Ring;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.ZBufferState;
import com.jmex.audio.AudioTrack;
import com.jmex.effects.particles.ParticleFactory;
import com.jmex.effects.particles.ParticleSystem;
import com.jmex.game.state.GameStateManager;
import com.jmex.jbullet.nodes.PhysicsVehicleNode;

import de.bazinga.rotw.collision.CollidableObjects;
import de.bazinga.rotw.effects.Explosion;
import de.bazinga.rotw.game.Game;
import de.bazinga.rotw.game.RotwGameState;
import de.bazinga.rotw.network.BasicSyncManager;
import de.bazinga.rotw.network.Client;
import de.bazinga.rotw.network.Server;
import de.bazinga.rotw.player.Player;
import de.bazinga.rotw.sound.Sound;
import de.bazinga.rotw.texture.TextureCache;

public abstract class Car extends Vehicle {
	private static final long serialVersionUID = 1L;
	
	protected float forwardSpeed = 0;
	

	protected PhysicsVehicleNode physicsCar;

    //temporary vector for the rotation
    protected static final Vector3f tempVa = new Vector3f();
	
	
	protected float acceleration = 150f;
	protected float turnSpeed = 0.8f;
	protected float brakeSpeed = 10f;
	
	
	
	protected Vector3f startLocation = null;
	
	
	protected int damage = 0;
	private long lastDamage = 0;
	private ParticleSystem smokeParticles = null;
	
	//private ParticleSystem explosionParticles = null;
	
    
	
	private boolean isCurrentlyUsed = false;
	private long startedUsing = 0;
	private int vehicleIndex = 0;
	
	protected AudioTrack at = Sound.getInstance().getAudioTrack("sounds/engineRunning.wav");
	
	
	

	public Car(Vector3f startLocation) {
		super();
		
		this.startLocation = startLocation;
		
		
		/**
		 * TODO
		 * - Kill People when the get hit with highspeed
		 * - sounds
		 * - add damage + smoke
		 */
		
		loadModel();
		
		
		/**
		 * Smoke Effect
		 */
		initSmoke();
		
		
		/**
		 * Init Explosion
		 */
		//initExplosion();
		

        this.setModelBound(new BoundingBox());
        this.updateModelBound();

        this.setLocalTranslation(startLocation.clone());
        
		CollidableObjects.getInstance().addObject("vehicle", this);
        setVehicleIndex(CollidableObjects.getInstance().getObjects("vehicle").size()-1);
	}
	
	
	protected abstract void loadModel();
	
	
	
	
	private void initSmoke() {
		smokeParticles = ParticleFactory.buildParticles("Smoke", 80);
		smokeParticles.setGeometry(new Ring(new Vector3f(0,0,0),
				new Vector3f(0,1f,0),
				0.1f,
				0.1f));
		smokeParticles.setEmissionDirection(new Vector3f(0,1,0.0f));
		
		
		
		float pi = 3.14159265f;
		smokeParticles.setMinimumAngle(0);
		smokeParticles.setMaximumAngle((pi)/2.0f);
		smokeParticles.setInitialVelocity(0.0004f);
		smokeParticles.setMinimumLifeTime(800.0f);
		smokeParticles.setMaximumLifeTime(1600.0f);
		smokeParticles.setStartSize(0.006f);
		smokeParticles.setEndSize(0.7f);
		smokeParticles.setStartColor(ColorRGBA.darkGray);
		smokeParticles.setEndColor(ColorRGBA.gray);
		smokeParticles.getParticleController().setControlFlow(false);
		smokeParticles.getParticleController().setRepeatType(
                Controller.RT_CYCLE);
        

		smokeParticles.setLocalTranslation(new Vector3f(0.3f,0.6f,1.3f));
		smokeParticles.setIsCollidable(false);
        
		
		BlendState bs = Game.getGame().getDisplay().getRenderer().createBlendState();
        bs.setBlendEnabled(true);
        bs.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
        bs.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha);
        bs.setTestEnabled(true);
        smokeParticles.setRenderState(bs);
        
		smokeParticles.setRenderState(
				TextureCache.getInstance().getTexture("texture/smoke_particle.png"));
		
		
		smokeParticles.setModelBound(new BoundingBox());
		smokeParticles.updateModelBound();
		
		
		ZBufferState zs = Game.getGame().getDisplay().getRenderer().createZBufferState();
	    zs.setWritable(false);
	    smokeParticles.setRenderState(zs);
		smokeParticles.updateRenderState();
	}
	
	
	public void moveForBackward(float time) {
		if(KeyBindingManager.getKeyBindingManager().isValidCommand("moveUp",true) ||
				KeyBindingManager.getKeyBindingManager().isValidCommand("moveDown",true)) {
	
			float speed = 150;
	    	if(KeyBindingManager.getKeyBindingManager().isValidCommand("moveDown",true))
				speed *= -1;

			physicsCar.accelerate(speed);
				
		}else{
			physicsCar.accelerate(0);
		}
	}

	public void moveLeftRight(float time) {
		// Left/Right
		if(KeyBindingManager.getKeyBindingManager().isValidCommand("moveLeft",true) ||
				KeyBindingManager.getKeyBindingManager().isValidCommand("moveRight",true)) {
			
			float steer = 0.8f;
			
			if(KeyBindingManager.getKeyBindingManager().isValidCommand("moveRight",true))
				steer *= -1f;
			
			physicsCar.steer(steer);
		}else {
			physicsCar.steer(0);
		}
		
		if(KeyBindingManager.getKeyBindingManager().isValidCommand("SPACE",true)) {
			physicsCar.brake(10);
		}else{
			physicsCar.brake(0);
		}
	}
	
	
	
	public void update(float tpf) {
		/**
		 * Play Engine-Sound?
		 */
		if((System.currentTimeMillis() - startedUsing) > 2000 && !at.isPlaying()) {
			at.setLooping(true);
			at.play();
		}
		
		
		// Damage if he is in water
		float waterHeight = 
			((RotwGameState)GameStateManager.getInstance().getChild("gameState")).getWaterHeight(getLocalTranslation());
		
		if((waterHeight + 1) > getLocalTranslation().getY()) {
			//player.getLocalTranslation().setY(waterHeight);
			setDamage(getDamage() + 10, true);
		}
		
		moveForBackward(tpf);
		moveLeftRight(tpf);
		
		if(KeyBindingManager.getKeyBindingManager().isValidCommand("SPACE",true)) {
			physicsCar.brake(10);
		}else{
			physicsCar.brake(0);
		}
		
	}
	
	
	public void reset() {
		this.damage = 0;
		this.setLocalTranslation(startLocation.clone());
		setCurrentlyUsed(false);
		
		physicsCar.accelerate(0f);
		physicsCar.steer(0f);
		physicsCar.setLocalRotation(new Matrix3f());
		
		if(physicsCar.hasChild(smokeParticles)) physicsCar.detachChild(smokeParticles);
	}
	
	
	public Vector3f getExitLocation() {
		Vector3f loc = getLocalTranslation().add(new Vector3f(0,2,0)).add(getLocalRotation().getRotationColumn(2,tempVa).cross(new Vector3f(0,1,0)).mult(3));
		return loc;
	}
	
	public Vector3f getCameraLocation() {
		return new Vector3f(
				getLocalTranslation().x,
				getLocalTranslation().y + 1.3f,
				getLocalTranslation().z);
	}
	
	
	public int getSpeed() {
		return 50;
	}


	public void handleEvent(InputActionEvent evt) {
		
	}


	public boolean isCurrentlyUsed() {
		return isCurrentlyUsed;
	}


	public void setCurrentlyUsed(boolean used) {
		isCurrentlyUsed = used;
		startedUsing = System.currentTimeMillis();
		if(!used && at.isPlaying()) {
			at.stop();
		}
	}


	public int getVehicleIndex() {
		return vehicleIndex;
	}


	public void setVehicleIndex(int index) {
		vehicleIndex = index;
	}


	public int getDamage() {
		return damage;
	}


	public void setDamage(int damage, boolean sendMsg) {
		Player player = ((RotwGameState)GameStateManager.getInstance().getChild("gameState")).getPlayer();
		
		// Play damage Sound
		if(this.damage < damage && player.hasVehicle() && player.getVehicle() == this) {
			if( (System.currentTimeMillis() - lastDamage) > 1500) {                
                Sound.getInstance().getAudioTrack("sounds/crash.wav").play();
                lastDamage = System.currentTimeMillis();
			}
		}
		
		this.damage = damage;
		
		if(this.damage > 50) {
			physicsCar.attachChild(smokeParticles);
		}
		
		if(this.damage >= 100) {
			// Player
			Node rootNode = ((RotwGameState)GameStateManager.getInstance().getChild("gameState")).getRootNode();
			
			Explosion explosion = Explosion.getUnusedExplosion(physicsCar.getLocalTranslation().add(new Vector3f(0.5f,1f,-1f)));
			rootNode.attachChild(explosion);
			explosion.getExplosionParticles().forceRespawn();
			explosion.hide();
			explosion.updateGeometricState(0, false);
			rootNode.updateGeometricState(0, false);
			
			Sound.getInstance().getAudioTrack("sounds/explosion.wav").play();
			
			if(player.hasVehicle() && player.getVehicle() == this) {
				player.setVehicle(null);
				//player.died("", player.getLocalTranslation());
				player.diedFalling();
			}
			this.reset();
		}
		
		// Send Damage to clients
		if(sendMsg) {
			BasicSyncManager syncManager = ((RotwGameState)GameStateManager.getInstance().getChild("gameState"))
				.getSyncManager();
			
			VehicleDamageMessage vdm = new VehicleDamageMessage();
			vdm.setVehicleIndex(getVehicleIndex());
			vdm.setDamage(damage);
			
			if(syncManager instanceof Server) {
				((Server)syncManager).getServer().sendToAll(vdm);
			}else if(syncManager instanceof Client) {
				((Client)syncManager).getClient().sendToServer(vdm);
			}
		}
	}
	
	public void setLocalTranslation(Vector3f trans) {
		if(physicsCar != null)
			physicsCar.setLocalTranslation(trans.x, trans.y, trans.z);
	}
	
	public Vector3f getLocalTranslation() {
		return physicsCar.getLocalTranslation();
	}
	
	
	public boolean isDriving() {
		if(physicsCar.getAngularVelocity().length() < 0.1f && 
				physicsCar.getAngularVelocity().length() > -0.1f) 
			return false;
        return true;
	}
	
	
	public float getTurnSpeed() {
		return turnSpeed;
	}


	public void setTurnSpeed(float turnSpeed) {
		this.turnSpeed = turnSpeed;
	}


	public PhysicsVehicleNode getPhysicsCar() {
		return physicsCar;
	}


	public void setPhysicsCar(PhysicsVehicleNode physicsCar) {
		this.physicsCar = physicsCar;
	}


	public float getAcceleration() {
		return acceleration;
	}


	public void setAcceleration(float acceleration) {
		this.acceleration = acceleration;
	}


	public float getBrakeSpeed() {
		return brakeSpeed;
	}


	public void setBrakeSpeed(float brakeSpeed) {
		this.brakeSpeed = brakeSpeed;
	}
}
